(C) 1993-1996 Stuart Cheshire <cheshire@cs.stanford.edu>
Don’t use ScrollRect on offscreen GWorlds; it is slower than the equivalent CopyBits call, and doesn't work properly on systems with more than one monitor. I wasted a lot of time tracking down this bug.
Even if you personally don't have more than one monitor, an increasing number of people do, especially with the new PowerBooks which, out of the box, will drive both the internal display and an external colour monitor simultaneously.
To understand the bug, first understand the cleverness of ScrollRect.
Imagine you scroll an image ten pixels up and ten pixels to the right. This leaves an empty white 'L' shaped region below and to the left of the scrolled area. Now imagine that the image is in a window which straddles two monitors, a colour monitor on the left and a monochrome one on the right.
It doesn't make sense to scroll black and white pixels across the boundary line onto the colour monitor, so ScrollRect effectively does a separate scroll for each monitor, resulting in two different empty white 'L' shaped regions. The updateRgn returned by ScrollRect is the union of these two regions, so you can simply pass it to InvalRgn and the correct areas of the window will be added to the updateRgn for redrawing on the next 'update' event. Marvelous!
However, now suppose that you are using ScrollRect to move pixels around on an off-screen GWorld. ScrollRect doesn't realize that an off-screen GWorld is not on the screen, and performs its normal cleverness regarding not scrolling pixels across the boundary between two monitors. Of course, there are no boundaries on the off-screen GWorld, but if you imagine super-imposing your monitor layout on top of your off-screen GWorld's coordinate system, then your image will develop 'tears' wherever it crosses a line corresponding to the boundary of two monitors on your desktop.
So, the moral is, when you are scrolling the contents of a *window* (in response to the user clicking the scroll bars), and you want the correct updateRgn afterwards so that the window will automatically redraw correctly, then ScrollRect will nicely cope with multiple monitors for you, but for other cases use CopyBits.
In case it is not obvious, here is the translation of the ScrollRect call:
When scrolling a window, there is one subtlety to take care of, which a shocking number of shipping programs get wrong.
Consider what happens if the window you are scrolling already requires updating, perhaps because part of it was just exposed after moving some other window, or because the update event resulting from a previous scroll has not yet been handled.
That means that the window's updateRgn will be non-empty, and there will be a little patch of white pixels somewhere in the window, waiting to be painted on the next "update" event.
Now imagine that you scroll the whole window, including this little patch of white pixels that are waiting to be painted. Now, the little patch of white pixels has moved. That means that the window's update region, which tells your program that this patch has to be repainted, should also move, to indicate the new location of the patch. ScrollRect will not do this for you automatically.
Fortunately the code to do all of this is not too complicated:
Now, if you are not scrolling the whole window, but only a portion of its content, it gets more tricky. You have to take the union of the region you're scrolling and the window's updateRgn, remove that region from the updateRgn (by calling ValidRgn), offset the region by the scroll amount, and add it back into the window's updateRgn (by calling InvalRgn). Yuck!